Go with Raylib

Setup

  • Install Go :

  • Init the module :

    • go mod init <project_name>

  • Install Raylib :

    • go get github.com/gen2brain/raylib-go/raylib

  • Get the latest RayLib release :

    • RayLib - Releases

    • The .dll  is required  to be placed at in the same folder as the main.go , or in the PATH .

  • File System :

     .
    ├──  raylib.dll // the downloaded raylib .dll
    ├──  main.go
    ├──  go.sum     // file created when installing dependencies.
    └──  go.mod     // file created when initializing the module.
    
  • Execute :

    • go run main.go

Moving Ball

/*
go run main.go
*/

package main

import (
    "github.com/gen2brain/raylib-go/raylib"
)

func main() {
    const SCREEN_WIDTH = 800
    const SCREEN_HEIGHT = 450

    rl.InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Raylib in Go | 1_moving_ball")
    rl.SetTargetFPS(60)

    // Initialize ball
    ball_position := rl.Vector2{X: SCREEN_WIDTH / 2.0, Y: SCREEN_HEIGHT / 2.0}

    for !rl.WindowShouldClose() {
        // Update ball position
        if rl.IsKeyDown(rl.KeyRight) {
            ball_position.X += 2.0
        }
        if rl.IsKeyDown(rl.KeyLeft) {
            ball_position.X -= 2.0
        }
        if rl.IsKeyDown(rl.KeyUp) {
            ball_position.Y -= 2.0
        }
        if rl.IsKeyDown(rl.KeyDown) {
            ball_position.Y += 2.0
        }

        // Draw
        rl.BeginDrawing()
        rl.ClearBackground(rl.RayWhite)

        rl.DrawText("move the ball with arrow keys", 10, 10, 20, rl.DarkGray)
        rl.DrawCircleV(ball_position, 50.0, rl.Maroon)

        rl.EndDrawing()
    }

    rl.CloseWindow()
}

Collect The Coins

/*
go run main.go
*/

package main

import (
    "fmt"
    "math/rand"
    "time"

    rl "github.com/gen2brain/raylib-go/raylib"
)

const (
    SCREEN_WIDTH  = 800
    SCREEN_HEIGHT = 600
    MAX_COINS     = 20
    PLAYER_SPEED  = 4.0
)

type Player struct {
    position rl.Vector2
    radius   float32
    score    int
}

type Coin struct {
    position rl.Vector2
    radius   float32
    active   bool
}

func main() {
    rl.InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Raylib in Go | 2_collect_the_coins")
    rl.SetTargetFPS(60)
    defer rl.CloseWindow()

    // Seed random generator
    rand.Seed(time.Now().UnixNano())

    // Initialize player
    player := Player{
        position: rl.NewVector2(SCREEN_WIDTH/2, SCREEN_HEIGHT/2),
        radius:   20,
        score:    0,
    }

    // Initialize coins
    var coins [MAX_COINS]Coin
    for i := 0; i < MAX_COINS; i++ {
        coins[i] = Coin{
            position: rl.NewVector2(float32(rand.Intn(SCREEN_WIDTH-40)+20), float32(rand.Intn(SCREEN_HEIGHT-40)+20)),
            radius:   8,
            active:   true,
        }
    }

    // Main game loop
    for !rl.WindowShouldClose() {
        // Update player
        if rl.IsKeyDown(rl.KeyW) {
            player.position.Y -= PLAYER_SPEED
        }
        if rl.IsKeyDown(rl.KeyS) {
            player.position.Y += PLAYER_SPEED
        }
        if rl.IsKeyDown(rl.KeyA) {
            player.position.X -= PLAYER_SPEED
        }
        if rl.IsKeyDown(rl.KeyD) {
            player.position.X += PLAYER_SPEED
        }

        // Check collisions
        for i := 0; i < MAX_COINS; i++ {
            if coins[i].active {
                dist := rl.Vector2Distance(player.position, coins[i].position)
                if dist < player.radius+coins[i].radius {
                    coins[i].active = false
                    player.score++
                }
            }
        }

        // Draw
        rl.BeginDrawing()
        rl.ClearBackground(rl.DarkGreen)

        rl.DrawCircleV(player.position, player.radius, rl.Blue)
        for i := 0; i < MAX_COINS; i++ {
            if coins[i].active {
                rl.DrawCircleV(coins[i].position, coins[i].radius, rl.Gold)
            }
        }
        rl.DrawText(fmt.Sprintf("Score: %d", player.score), 10, 10, 20, rl.White)

        rl.EndDrawing()
    }
}

Space Invaders

/*
go run main.go
*/

package main

import (
    "fmt"
    "math/rand"
    "time"

    rl "github.com/gen2brain/raylib-go/raylib"
)

const (
    SCREEN_WIDTH  = 800
    SCREEN_HEIGHT = 600
    MAX_BULLETS   = 64
    BULLET_SPEED  = 6.0
)

type Player struct {
    Position rl.Vector2
    Radius   float32
}

type Bullet struct {
    Position rl.Vector2
    Velocity rl.Vector2
    Radius   float32
    Active   bool
}

type Enemy struct {
    Position rl.Vector2
    Radius   float32
    Active   bool
    Color    rl.Color
}

func spawnBullet(bullets []Bullet, pos rl.Vector2) {
    for i := range bullets {
        if !bullets[i].Active {
            bullets[i].Active = true
            bullets[i].Position = pos
            bullets[i].Velocity = rl.Vector2{X: 0, Y: -BULLET_SPEED}
            bullets[i].Radius = 5
            return
        }
    }
}

func spawnInactiveEnemies(enemies []Enemy) {
    for i := range enemies {
        if !enemies[i].Active {
            enemies[i].Position = rl.Vector2{
                X: float32(rand.Intn(701) + 50),
                Y: float32(rand.Intn(251) + 50),
            }
            enemies[i].Radius = 15
            enemies[i].Active = true
            enemies[i].Color = rl.NewColor(
                uint8(rand.Intn(156)+100),
                uint8(rand.Intn(156)+100),
                uint8(rand.Intn(156)+100),
                255,
            )
        }
    }
}

func main() {
    rand.Seed(time.Now().UnixNano())
    rl.InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Raylib in Go | 3_space_invaders")
    rl.SetTargetFPS(60)

    player := Player{Position: rl.Vector2{X: 400, Y: 500}, Radius: 20}
    var bullets [MAX_BULLETS]Bullet // Stack allocation.

    enemiesCount := 20
    enemies := make([]Enemy, enemiesCount) // Heap allocation.
        // No need to free this, as there's Go is garbage collected. You can't manually free it.
    enemiesDestroyedCount := 0
    spawnInactiveEnemies(enemies)

    for !rl.WindowShouldClose() {
        // Player movement
        if rl.IsKeyDown(rl.KeyA) {
            player.Position.X -= 4
        }
        if rl.IsKeyDown(rl.KeyD) {
            player.Position.X += 4
        }
        if rl.IsKeyDown(rl.KeyW) {
            player.Position.Y -= 4
        }
        if rl.IsKeyDown(rl.KeyS) {
            player.Position.Y += 4
        }
        if rl.IsKeyPressed(rl.KeySpace) {
            spawnBullet(bullets[:], player.Position)
        }

        // Update bullets
        for i := range bullets {
            if bullets[i].Active {
                bullets[i].Position.X += bullets[i].Velocity.X
                bullets[i].Position.Y += bullets[i].Velocity.Y

                if bullets[i].Position.Y < 0 {
                    bullets[i].Active = false
                }
            }
        }

        // Update enemies
        activeEnemies := 0
        for i := range enemies {
            if enemies[i].Active {
                enemies[i].Position.Y += 0.5
                activeEnemies++
            }
        }

        // Respawn inactive enemies if too few
        if activeEnemies < 5 {
            spawnInactiveEnemies(enemies)
        }

        // Check collisions
        for i := range bullets {
            if !bullets[i].Active {
                continue
            }
            for j := range enemies {
                if !enemies[j].Active {
                    continue
                }
                d := rl.Vector2Distance(bullets[i].Position, enemies[j].Position)
                r := bullets[i].Radius + enemies[j].Radius
                if d < r {
                    bullets[i].Active = false
                    enemies[j].Active = false
                    enemiesDestroyedCount++
                }
            }
        }

        // Drawing
        rl.BeginDrawing()
        rl.ClearBackground(rl.Black)

        rl.DrawCircleV(player.Position, player.Radius, rl.Blue)

        for i := range bullets {
            if bullets[i].Active {
                rl.DrawCircleV(bullets[i].Position, bullets[i].Radius, rl.Yellow)
            }
        }
        for i := range enemies {
            if enemies[i].Active {
                rl.DrawCircleV(enemies[i].Position, enemies[i].Radius, enemies[i].Color)
            }
        }

        rl.DrawText("Space Invaders: WASD move | SPACE shoot", 10, 10, 20, rl.White)
        rl.DrawText(fmt.Sprintf("Enemies destroyed: %d", enemiesDestroyedCount), 10, 40, 20, rl.Yellow)

        rl.EndDrawing()
    }

    rl.CloseWindow()
}